
#include <BFont>
#include <BIcon>
#include <BPainter>
#include <BMouseEvent>

#include "member_BMenuBar.h"
#include "member_BStyle.h"

using namespace BWE;

#define member					(*(member_BMenuBar*)_ptr)
#define member_allocate()		_ptr = new member_BMenuBar(this)
#define member_release()		delete (member_BMenuBar*)_ptr

BMenuBar::BMenuBar(BWidget* parent) : BWidget(parent)
{
	member_allocate();
}
BMenuBar::~BMenuBar()
{
	for (int i = 0; i < member.items.size(); i++)
	{
		MenuBarItem& item = member.items[i];
		member.disconnect(item.menu);
	}
	member_release();
}

const BString& BMenuBar::realTips() const
{
	if (member.hoverIndex >= 0)
	{
		MenuBarItem& item = member.items.at(member.hoverIndex);
		if (item.menu)
		{
			const BString& tips = item.menu->realTips();
			if (tips.size())
				return tips;
		}
		return item.tips;
	}
	return BWidget::realTips();
}

void BMenuBar::setAlign(Align align)
{
	if (member.align != align)
	{
		member.align = align;
		member.dirty = true;
		this->emit(Signal_AlignChanged, align);
		this->fresh();
	}
}
Align BMenuBar::align() const
{
	return member.align;
}

void BMenuBar::setForm(Form form)
{
	if (member.form != form)
	{
		member.form = form;
		member.dirty = true;
		this->emit(Signal_FormChanged, form);
		this->fresh();
	}
}
Form BMenuBar::form() const
{
	return member.form;
}

void BMenuBar::setSpacing(int spacing)
{
	if (member.spacing != spacing)
	{
		member.spacing = spacing;
		member.dirty = true;
		this->emit(Signal_SpacingChanged, spacing);
		this->fresh();
	}
}
int BMenuBar::spacing() const
{
	return member.spacing;
}

void BMenuBar::setOrientation(Orientation orientation)
{
	if (member.orientation != orientation)
	{
		member.orientation = orientation;
		member.dirty = true;
		this->emit(Signal_OrientationChanged, orientation);
		this->fresh();
	}
}
Orientation BMenuBar::orientation() const
{
	return member.orientation;
}

void BMenuBar::addMenu(BMenu* menu)
{
	if (this->contain(menu))
		return;
	this->addChild(menu);
	menu->hide();
	member.items.append(menu);
	member.connect(menu, Signal_Enabled, &member_BMenuBar::slotMenuEnabled);
	member.connect(menu, Signal_Renamed, &member_BMenuBar::slotMenuRenamed);
	member.connect(menu, Signal_Styled, &member_BMenuBar::slotMenuStyled);
	member.connect(menu, Signal_IconChanged, &member_BMenuBar::slotMenuIconChanged);
	member.connect(menu, Signal_TitleChanged, &member_BMenuBar::slotMenuTitleChanged);
	member.dirty = true;
	this->fresh();
}
BMenu* BMenuBar::addMenu(const BString& title)
{
	for (int i = 0; i < member.items.size(); i++)
	{
		if (member.items[i].menu == 0)
			continue;
		if (member.items[i].menu->title() == title)
			return 0;
	}
	BMenu* menu = new BMenu(title);
	addMenu(menu);
	return menu;
}

void BMenuBar::insertMenu(int index, BMenu* menu)
{
	if (!this->contain(menu))
	{
		this->addChild(menu);
		menu->hide();
		member.items.insert(index, menu);
		member.connect(menu, Signal_Enabled, &member_BMenuBar::slotMenuEnabled);
		member.connect(menu, Signal_Renamed, &member_BMenuBar::slotMenuRenamed);
		member.connect(menu, Signal_Styled, &member_BMenuBar::slotMenuStyled);
		member.connect(menu, Signal_IconChanged, &member_BMenuBar::slotMenuIconChanged);
		member.connect(menu, Signal_TitleChanged, &member_BMenuBar::slotMenuTitleChanged);
		member.dirty = true;
		this->fresh();
	}
}
BMenu* BMenuBar::insertMenu(int index, const BString& title)
{
	if (index < 0)
		return 0;
	for (int i = 0; i < member.items.size(); i++)
	{
		if (member.items[i].menu->title() == title)
			return 0;
	}
	BMenu* menu = new BMenu(title);
	insertMenu(index, menu);
	return menu;
}

bool BMenuBar::removeMenu(BMenu* menu)
{
	if (member.items.remove(menu))
	{
		this->removeChild(menu);
		member.disconnect(menu);
		member.dirty = true;
		this->fresh();
		return true;
	}
	return false;
}
bool BMenuBar::clearMenus()
{
	if (member.items.size())
	{
		while (member.items.size())
		{
			MenuBarItem& last = member.items.last();
			if (last.menu)
			{
				this->removeChild(last.menu);
				member.disconnect(last.menu);
			}
			member.items.remove(member.items.size() - 1);
		}
		member.dirty = true;
		this->fresh();
	}
	return true;
}

int BMenuBar::menuCount() const
{
	return member.items.size();
}
BMenu* BMenuBar::menu(int index)
{
	return member.items(index).menu;
}
const BMenu* BMenuBar::menu(int index) const
{
	return member.items(index).menu;
}

bool BMenuBar::addSeparator()
{
	if (member.items.append(MenuBarItem()))
	{
		member.dirty = true;
		this->fresh();
		return true;
	}
	return false;
}
bool BMenuBar::insertSeparator(int index)
{
	if (0 <= index && index <= member.items.size())
	{
		member.items.insert(index);
		member.dirty = true;
		this->fresh();
		return true;
	}
	return false;
}
bool BMenuBar::insertSeparator(const BMenu* before)
{
	if (!before)
		return false;
	int index = member.items.find(before);
	if (index < member.items.size())
	{
		member.items.insert(index);
		member.dirty = true;
		this->fresh();
		return true;
	}
	return false;
}

bool BMenuBar::isSeparator(int index)
{
	if (member.items.check(index))
	{
		MenuBarItem& item = member.items[index];
		return item.menu == 0;
	}
	return false;
}
bool BMenuBar::removeSeparator(int index)
{
	if (member.items.check(index))
	{
		MenuBarItem& item = member.items[index];
		if (item.menu == 0)
		{
			member.items.remove(index);
			member.dirty = true;
			this->adjust();
			this->fresh();
			return true;
		}
	}
	return false;
}

BSize BMenuBar::sizeHint() const
{
	if (member.dirty)
	{
		member.freshRects();
		member.dirty = false;
	}
	return this->preferSize(member.contentSize);
}

void BMenuBar::resizeEvent(const BEvent& event)
{
	member.dirty = true;
	BWidget::resizeEvent(event);
}

void BMenuBar::styleEvent(const BEvent& event)
{
	BWidget::styleEvent(event);
	const BStyle* style = event.value();
	member.separatorImage = 0;

	style_member(style)->assign(Value_Align, member.align);
	style_member(style)->assign(Value_Form, member.form);
	style_member(style)->assign(Value_Spacing, member.spacing);
	style_member(style)->assign(Value_Orientation, member.orientation);

	if (const BStyle* separatorStyle = style->annex("separator"))
	{
		member.separatorStyle = separatorStyle;
		style_member(separatorStyle)->assign(Value_Width, member.separatorSize.width());
		style_member(separatorStyle)->assign(Value_Height, member.separatorSize.height());
		if (BImage* image = separatorStyle->value(Value_Image))
			member.separatorImage = image;
	}

	for (int i = 0; i < member.items.size(); i++)
	{
		if (member.items[i].menu == 0)
			continue;
		member.items[i].dirtyStyle = true;
		member.items[i].dirty = true;
	}
	member.dirty = true;
}
void BMenuBar::freshEvent(const BEvent& event)
{
	if (member.dirty)
	{
		member.freshRects();
		member.dirty = false;
	}
	BWidget::freshEvent(event);
}
void BMenuBar::paintEvent(const BEvent& event)
{
	BPainter painter(this);
	painter.drawBackground();
	painter.drawForeground();

	const BRect& crect = this->centerRect();
	State state = painter.state();
	for (int i = 0; i < member.items.size(); i++)
	{
		const MenuBarItem& item = member.items[i];
		const BRect rect = item.rect();
		if (const BMenu* menu = item.menu)
		{
			State itemState = state;
			if (!menu->enabled())
			{
				itemState = State_Disable;
			}
			else if (member.hoverIndex == i)
			{
				if (member.pressed)
					itemState |= State_Pressed;
				else
					itemState |= State_Hovered;
			}
			painter.setStyle(item.realStyle);
			painter.pushMatrix();
			painter.translate(item.pos);
			//painter.setClip(item_member(item)->rect() & crect);
			item.drawBackground(painter, itemState);
			item.drawMargin(painter, itemState);
			item.drawBorder(painter, itemState);
			item.drawContent(painter, itemState);
			painter.popMatrix();
			painter.setStyle(0);
		}
		else
		{
			if (item.ia_textRect.empty())
			{
				if (member.separatorImage)
				{
					painter.drawImage(item.rect(), member.separatorImage);
				}
				else if (painter.setColor(Color_Separator))
				{
					painter.setLineWidth((float)member.separatorSize.height());
					painter.drawLine(rect.leftCenter(), rect.rightCenter());
				}
				continue;
			}
			if (member.separatorImage)
			{
				painter.drawImage(rect, member.separatorImage);
			}
			else if (painter.setColor(Color_Separator))
			{
				BPoint p0 = item.ia_textRect.leftCenter() - BPoint(member.spacing, 0);
				BPoint p1 = item.ia_textRect.rightCenter() + BPoint(member.spacing, 0);
				painter.setLineWidth((float)member.separatorSize.width());
				painter.drawLine(rect.leftCenter(), p0);
				painter.drawLine(rect.rightCenter(), p1);
			}
			item.drawContent(painter, state);
		}
	}

	painter.drawPerch();
	painter.drawMargin();
	painter.drawBorder();
}

void BMenuBar::mouseEnterEvent(const BMouseEvent& mouseEvent)
{
	if (member.hoverIndex >= 0)
	{
		member.hoverIndex = -1;
		this->fresh();
	}
	BWidget::mouseEnterEvent(mouseEvent);
}
void BMenuBar::mouseLeaveEvent(const BMouseEvent& mouseEvent)
{
	if (member.hoverIndex >= 0)
	{
		member.hoverIndex = -1;
		this->fresh();
	}
	BWidget::mouseLeaveEvent(mouseEvent);
}

void BMenuBar::mousePressEvent(const BMouseEvent& mouseEvent)
{
	if (mouseEvent.button() == Button_Left)
	{
		if (member.items.check(member.hoverIndex))
		{
			member.pressed = true;
			this->fresh();
		}
	}
	BWidget::mousePressEvent(mouseEvent);
}
void BMenuBar::mouseReleaseEvent(const BMouseEvent& mouseEvent)
{
	if (mouseEvent.button() == Button_Left)
	{
		if (member.pressed)
		{
			member.pressed = false;
			this->fresh();
		}
	}
	BWidget::mouseReleaseEvent(mouseEvent);
}
void BMenuBar::mouseMoveEvent(const BMouseEvent& mouseEvent)
{
	int hoverIndex = -1;
	const BPoint& pos = mouseEvent.pos();
	for (int i = 0; i < member.items.size(); i++)
	{
		if (member.items[i].rect().contain(pos))
		{
			hoverIndex = i;
			break;
		}
	}
	if (member.hoverIndex != hoverIndex)
	{
		member.hoverIndex = hoverIndex;
		this->fresh();
	}
	BWidget::mouseMoveEvent(mouseEvent);
}

void BMenuBar::mouseClickEvent(const BMouseEvent& mouseEvent)
{
	if (member.hoverIndex > -1)
	{
		BMenu* menu = member.items[member.hoverIndex].menu;
		if (menu->visible())
		{
			menu->hide();
		}
		else if (menu->enabled())
		{
			BRect itemRect = member.items[member.hoverIndex].rect();
			itemRect += menu->offset();
			BSize size = menu->sizeHint();
			BRect rootRect = this->mapFrom(this->root(), this->root()->size());
			BRect rect = this->rect();
			if (member.orientation == Orientation_Horizontal)
			{
				int spaceDown = rootRect.bottom() - rect.bottom();
				if (spaceDown < size.height())
				{
					menu->setPos(itemRect.left(), itemRect.top() - size.height());
				}
				else
				{
					menu->setPos(itemRect.left(), itemRect.bottom());
				}
				if (size.width() > rootRect.width())
					size.width() = rootRect.width();
				menu->setSize(size);
				menu->setParent(this);
				menu->popup();
			}
			if (member.orientation == Orientation_Vertical)
			{
				int spaceRight = rootRect.right() - rect.right();
				if (spaceRight < size.width())
				{
					menu->setPos(itemRect.left() - size.width(), itemRect.top());
				}
				else
				{
					menu->setPos(itemRect.right(), itemRect.top());
				}
				if (size.width() > rootRect.width())
					size.width() = rootRect.width();
				menu->setSize(size);
				menu->setParent(this);
				menu->popup();
			}

		}
		this->fresh();
	}
	BWidget::mouseClickEvent(mouseEvent);
}
